/*
 * FocalCalculations.java
 *
 * Created on February 8, 2005, 3:01 PM
 */

package edu.temple.GUS.MMA.Functions;

import edu.temple.GUS.MMA.DataTypes.*;
import edu.temple.GUS.MMA.Neighborhoods.*;
import edu.temple.GUS.MMA.Exceptions.MMAFunctionInitException;
import edu.temple.GUS.MMA.util.StatisticalOperator;

/**
 *
 * @author  jeffleon
 * This class contains all functions that perform focal calculations for an element of a given datatype.
 */

class FocalCalculations {
    
    // The supported operator types 
    public static final int OPERATOR_ADD = 1;
    public static final int OPERATOR_MEAN = 2;
    public static final int OPERATOR_MEDIAN = 3;
    public static final int OPERATOR_MAXIMUM = 4;
    public static final int OPERATOR_MINIMUM = 5;
    public static final int OPERATOR_RANGE = 6;
    public static final int OPERATOR_VARIANCE = 7;
    
    /** Creates a new instance of FocalCalculations */
    public FocalCalculations() {}
    
    // Function to perform the focal calculation for a time series object
    public float execute( int operator, MMATimeSeries tSeries, int currentIndex, int tLowerBoundaryIndex, int tUpperBoundaryIndex)
    {
        float result = 0;
        int t = tSeries.getT();
        float noData = tSeries.getNoData();
        boolean bAllNoData = true;

        switch (operator)
        {
            case OPERATOR_ADD:
                for ( int i = currentIndex + tLowerBoundaryIndex; i <= currentIndex + tUpperBoundaryIndex; i++)
                {
                    if (( i >= 0) && (i < t))
                        if (tSeries.getElement(i) != noData)
                        {
                            bAllNoData = false;
                            result += tSeries.getElement(i);
                        }
                }
                
            // Return noData if all there is no values in all cells
            if (bAllNoData)
                result = noData;
                        
            break;
            
            default:
                
                float temp[] = new float[t];
                int count = 0;
                
                for ( int i = currentIndex + tLowerBoundaryIndex; i <= currentIndex + tUpperBoundaryIndex; i++)
                {
                    if (( i >= 0) && (i < t))
                        if (tSeries.getElement(i) != noData)
                            temp[count++] = tSeries.getElement(i);

                }
                
                if (operator == OPERATOR_MEAN)
                    result = StatisticalOperator.mean(temp,count);
                if (operator == OPERATOR_MEDIAN)
                    result = StatisticalOperator.median(temp,count);
                if (operator == OPERATOR_MAXIMUM)
                    result = StatisticalOperator.max(temp,count);
                if (operator == OPERATOR_MINIMUM)
                    result = StatisticalOperator.min(temp);
                if (operator == OPERATOR_RANGE)
                    result = StatisticalOperator.max(temp,count) - StatisticalOperator.min(temp);
                if (operator == OPERATOR_VARIANCE)
                    result = StatisticalOperator.variance(temp,count);
                
            break;
                
        }

        return result;
    }

    // Function to perform the focal calculation for a one element of a grid object
    // CurrentXIndex and CurrentYIndex defines the location of the element.
    // XYRectangle specifies the neighborhood where the calculation is being performed on.
    public float execute( int operator, MMAGrid grid, int currentXIndex, int currentYIndex, XYRectangle rect)
    {
        float result = 0;
        int x = grid.getX();
        int y = grid.getY();
        float noData = grid.getNoData();
        boolean bAllNoData = true;
        
        // Defines the boundaries of the focal neighborhood
        // where the center of the neighborhood defines the origin
        // i.e. the indexes are zero
        
        // Get boundaries in x-direction in terms of index
        int xLowerBoundaryIndex = rect.getXLowerBoundary();
        int xUpperBoundaryIndex = rect.getXUpperBoundary();

        // Get boundaries in y-direction in terms of index
        int yLowerBoundaryIndex = rect.getYLowerBoundary();
        int yUpperBoundaryIndex = rect.getYUpperBoundary();

        switch (operator)
        {
            case OPERATOR_ADD:
                for ( int j = currentYIndex + yLowerBoundaryIndex; j <= currentYIndex + yUpperBoundaryIndex; j++)
                    for ( int i = currentXIndex + xLowerBoundaryIndex; i <= currentXIndex + xUpperBoundaryIndex; i++)
                    {
                        if ((( i >= 0) && (i < x)) && (( j >= 0) && (j < y)))
                            if (grid.getElement(i,j) != noData)
                            {
                                bAllNoData = false;
                                result += grid.getElement(i,j);
                            }
                    }

            // Return noData if all there is no values in all cells
            if (bAllNoData)
                result = noData;
            break;
            
            default:
                
                float temp[] = new float[x*y];
                int count = 0;
                
                for ( int j = currentYIndex + yLowerBoundaryIndex; j <= currentYIndex + yUpperBoundaryIndex; j++)
                    for ( int i = currentXIndex + xLowerBoundaryIndex; i <= currentXIndex + xUpperBoundaryIndex; i++)
                    {
                        if ((( i >= 0) && (i < x)) && (( j >= 0) && (j < y)))
                            if (grid.getElement(i,j) != noData)
                                temp[count++] = grid.getElement(i,j);
                    }
                
                if (operator == OPERATOR_MEAN)
                    result = StatisticalOperator.mean(temp,count);
                if (operator == OPERATOR_MEDIAN)
                    result = StatisticalOperator.median(temp,count);
                if (operator == OPERATOR_MAXIMUM)
                    result = StatisticalOperator.max(temp,count);
                if (operator == OPERATOR_MINIMUM)
                    result = StatisticalOperator.min(temp);
                if (operator == OPERATOR_RANGE)
                    result = StatisticalOperator.max(temp,count) - StatisticalOperator.min(temp);
                if (operator == OPERATOR_VARIANCE)
                    result = StatisticalOperator.variance(temp,count);
                
            break;
        }

        return result;
    }

    // Function to perform the focal calculation for a one element of a timecube object
    // CurrentXIndex, CurrentYIndex and currentTIndex defines the location of the element.
    // XYTRectangle specifies the neighborhood where the calculation is being performed on.
    public float execute(int operator, MMATimeCube cube, int currentXIndex, int currentYIndex, int currentTIndex, XYTRectangle rect)
    {
        float result = 0;
        int x = cube.getX();
        int y = cube.getY();
        int t = cube.getT();
        float noData = cube.getNoData();
        boolean bAllNoData = true;
        
        // Defines the boundaries of the focal neighborhood
        // where the center of the neighborhood defines the origin
        // i.e. the indexes are zero
        
        // Get boundaries in x-direction in terms of index
        int xLowerBoundaryIndex = rect.getXLowerBoundary();
        int xUpperBoundaryIndex = rect.getXUpperBoundary();

        // Get boundaries in y-direction in terms of index
        int yLowerBoundaryIndex = rect.getYLowerBoundary();
        int yUpperBoundaryIndex = rect.getYUpperBoundary();

        // Get boundaries in t-direction in terms of index
        int tLowerBoundaryIndex = rect.getTLowerBoundary();
        int tUpperBoundaryIndex = rect.getTUpperBoundary();

        switch (operator)
        {
            case OPERATOR_ADD:
                for ( int k = currentTIndex + tLowerBoundaryIndex; k <= currentTIndex + tUpperBoundaryIndex; k++)
                    for ( int j = currentYIndex + yLowerBoundaryIndex; j <= currentYIndex + yUpperBoundaryIndex; j++)
                        for ( int i = currentXIndex + xLowerBoundaryIndex; i <= currentXIndex + xUpperBoundaryIndex; i++)
                        {
                            if ((( i >= 0) && (i < x)) && (( j >= 0) && (j < y)) && (( k >= 0) && (k < t)))
                                if (cube.getElement(i,j,k) != noData)
                                {
                                    bAllNoData = false;
                                    result += cube.getElement(i,j,k);
                                }
                        }
                
            // Return noData if all there is no values in all cells
            if (bAllNoData)
                result = noData;

            break;
            
            default:

                float temp[] = new float[x*y*t];
                int count = 0;
                
                for ( int k = currentTIndex + tLowerBoundaryIndex; k <= currentTIndex + tUpperBoundaryIndex; k++)
                    for ( int j = currentYIndex + yLowerBoundaryIndex; j <= currentYIndex + yUpperBoundaryIndex; j++)
                        for ( int i = currentXIndex + xLowerBoundaryIndex; i <= currentXIndex + xUpperBoundaryIndex; i++)
                        {
                            if ((( i >= 0) && (i < x)) && (( j >= 0) && (j < y)) && (( k >= 0) && (k < t)))
                                if (cube.getElement(i,j,k) != noData)
                                    temp[count++] = cube.getElement(i,j,k);
                        }
                
                if (operator == OPERATOR_MEAN)
                    result = StatisticalOperator.mean(temp,count);
                if (operator == OPERATOR_MEDIAN)
                    result = StatisticalOperator.median(temp,count);
                if (operator == OPERATOR_MAXIMUM)
                    result = StatisticalOperator.max(temp,count);
                if (operator == OPERATOR_MINIMUM)
                    result = StatisticalOperator.min(temp);
                if (operator == OPERATOR_RANGE)
                    result = StatisticalOperator.max(temp,count) - StatisticalOperator.min(temp);
                if (operator == OPERATOR_VARIANCE)
                    result = StatisticalOperator.variance(temp,count);
    
            break;
        }
        
        return result;
    }

    // Function to perform the focal calculation for a one element of a spacecube object
    // CurrentXIndex, CurrentYIndex and currentZIndex defines the location of the element.
    // XYZRectangle specifies the neighborhood where the calculation is being performed on.
    public float execute( int operator, MMASpaceCube cube, int currentXIndex, int currentYIndex, int currentZIndex, XYZRectangle rect)
    {
        float result = 0;
        int x = cube.getX();
        int y = cube.getY();
        int z = cube.getZ();
        float noData = cube.getNoData();
        boolean bAllNoData = true;
        
        // Defines the boundaries of the focal neighborhood
        // where the center of the neighborhood defines the origin
        // i.e. the indexes are zero
        
        // Get boundaries in x-direction in terms of index
        int xLowerBoundaryIndex = rect.getXLowerBoundary();
        int xUpperBoundaryIndex = rect.getXUpperBoundary();

        // Get boundaries in y-direction in terms of index
        int yLowerBoundaryIndex = rect.getYLowerBoundary();
        int yUpperBoundaryIndex = rect.getYUpperBoundary();

        // Get boundaries in z-direction in terms of index
        int zLowerBoundaryIndex = rect.getZLowerBoundary();
        int zUpperBoundaryIndex = rect.getZUpperBoundary();

        switch (operator)
        {
            case OPERATOR_ADD:
                for ( int k = currentZIndex + zLowerBoundaryIndex; k <= currentZIndex + zUpperBoundaryIndex; k++)
                        for ( int j = currentYIndex + yLowerBoundaryIndex; j <= currentYIndex + yUpperBoundaryIndex; j++)
                            for ( int i = currentXIndex + xLowerBoundaryIndex; i <= currentXIndex + xUpperBoundaryIndex; i++)
                            {
                                if ((( i >= 0) && (i < x)) && (( j >= 0) && (j < y)) && (( k >= 0) && (k < z)))
                                    if (cube.getElement(i,j,k) != noData)
                                    {
                                        bAllNoData = false;
                                        result += cube.getElement(i,j,k);
                                    }
                            }
                
            // Return noData if all there is no values in all cells
            if (bAllNoData)
                result = noData;
                
            break;

            default:

                float temp[] = new float[x*y*z];
                int count = 0;
                
                for ( int k = currentZIndex + zLowerBoundaryIndex; k <= currentZIndex + zUpperBoundaryIndex; k++)
                    for ( int j = currentYIndex + yLowerBoundaryIndex; j <= currentYIndex + yUpperBoundaryIndex; j++)
                        for ( int i = currentXIndex + xLowerBoundaryIndex; i <= currentXIndex + xUpperBoundaryIndex; i++)
                        {
                            if ((( i >= 0) && (i < x)) && (( j >= 0) && (j < y)) && (( k >= 0) && (k < z)))
                                if (cube.getElement(i,j,k) != noData)
                                    temp[count++] = cube.getElement(i,j,k);
                        }
                
                if (operator == OPERATOR_MEAN)
                    result = StatisticalOperator.mean(temp,count);
                if (operator == OPERATOR_MEDIAN)
                    result = StatisticalOperator.median(temp,count);
                if (operator == OPERATOR_MAXIMUM)
                    result = StatisticalOperator.max(temp,count);
                if (operator == OPERATOR_MINIMUM)
                    result = StatisticalOperator.min(temp);
                if (operator == OPERATOR_RANGE)
                    result = StatisticalOperator.max(temp,count) - StatisticalOperator.min(temp);
                if (operator == OPERATOR_VARIANCE)
                    result = StatisticalOperator.variance(temp,count);
    
            break;
        }

        return result;
    }

    // Function to perform the focal calculation for a one element of a hypercube object
    // CurrentXIndex, CurrentYIndex and currentZIndex defines the location of the element.
    // XYZTRectangle specifies the neighborhood where the calculation is being performed on.
    public float execute( int operator, MMAHyperCube cube, int currentXIndex, int currentYIndex, int currentZIndex, int currentTIndex, XYZTRectangle rect)
    {
        float result = 0;
        int x = cube.getX();
        int y = cube.getY();
        int z = cube.getZ();
        int t = cube.getT();
        float noData = cube.getNoData();
        boolean bAllNoData = true;
        
        // Defines the boundaries of the focal neighborhood
        // where the center of the neighborhood defines the origin
        // i.e. the indexes are zero
        
        // Get boundaries in x-direction in terms of index
        int xLowerBoundaryIndex = rect.getXLowerBoundary();
        int xUpperBoundaryIndex = rect.getXUpperBoundary();

        // Get boundaries in y-direction in terms of index
        int yLowerBoundaryIndex = rect.getYLowerBoundary();
        int yUpperBoundaryIndex = rect.getYUpperBoundary();

        // Get boundaries in z-direction in terms of index
        int zLowerBoundaryIndex = rect.getZLowerBoundary();
        int zUpperBoundaryIndex = rect.getZUpperBoundary();

        // Get boundaries in t-direction in terms of index
        int tLowerBoundaryIndex = rect.getTLowerBoundary();
        int tUpperBoundaryIndex = rect.getTUpperBoundary();

        switch (operator)
        {
            case OPERATOR_ADD:
                for ( int l = currentTIndex + tLowerBoundaryIndex; l <= currentTIndex + tUpperBoundaryIndex; l++)
                    for ( int k = currentZIndex + zLowerBoundaryIndex; k <= currentZIndex + zUpperBoundaryIndex; k++)
                        for ( int j = currentYIndex + yLowerBoundaryIndex; j <= currentYIndex + yUpperBoundaryIndex; j++)
                            for ( int i = currentXIndex + xLowerBoundaryIndex; i <= currentXIndex + xUpperBoundaryIndex; i++)
                            {
                                if ((( i >= 0) && (i < x)) && (( j >= 0) && (j < y)) && (( k >= 0) && (k < z)) && (( l >= 0) && (l < t)))
                                    if (cube.getElement(i,j,k,l) != noData)
                                    {
                                        bAllNoData = false;
                                        result += cube.getElement(i,j,k,l);
                                    }
                            }
                
            // Return noData if all there is no values in all cells
            if (bAllNoData)
                result = noData;

            break;

            default:
                
                float temp[] = new float[x*y*z*t];
                int count = 0;

                for ( int l = currentTIndex + tLowerBoundaryIndex; l <= currentTIndex + tUpperBoundaryIndex; l++)
                    for ( int k = currentZIndex + zLowerBoundaryIndex; k <= currentZIndex + zUpperBoundaryIndex; k++)
                        for ( int j = currentYIndex + yLowerBoundaryIndex; j <= currentYIndex + yUpperBoundaryIndex; j++)
                            for ( int i = currentXIndex + xLowerBoundaryIndex; i <= currentXIndex + xUpperBoundaryIndex; i++)
                            {
                                if ((( i >= 0) && (i < x)) && (( j >= 0) && (j < y)) && (( k >= 0) && (k < z)) && (( l >= 0) && (l < t)))
                                    if (cube.getElement(i,j,k,l) != noData)
                                        temp[count++] = cube.getElement(i,j,k,l);
                            }
                
                if (operator == OPERATOR_MEAN)
                    result = StatisticalOperator.mean(temp,count);
                if (operator == OPERATOR_MEDIAN)
                    result = StatisticalOperator.median(temp,count);
                if (operator == OPERATOR_MAXIMUM)
                    result = StatisticalOperator.max(temp,count);
                if (operator == OPERATOR_MINIMUM)
                    result = StatisticalOperator.min(temp);
                if (operator == OPERATOR_RANGE)
                    result = StatisticalOperator.max(temp,count) - StatisticalOperator.min(temp);
                if (operator == OPERATOR_VARIANCE)
                    result = StatisticalOperator.variance(temp,count);
    
            break;
        }

        return result;
    }

    // Beginning of circular neighborhood type
    
    // Function to perform the focal calculation for a one element of a grid object
    // CurrentXIndex and CurrentYIndex defines the location of the element.
    // XYCircle specifies the neighborhood where the calculation is being performed on.
    public float execute( int operator, MMAGrid grid, int currentXIndex, int currentYIndex, XYCircle circle)
    {
        float result = 0;
        int x = grid.getX();
        int y = grid.getY();
        float noData = grid.getNoData();
        
        // Defines the boundaries of the focal neighborhood
        // where the center of the neighborhood defines the origin
        // i.e. the indexes are zero
        
        // Get boundaries in x-direction in terms of index
        int xLowerBoundaryIndex = circle.getXLowerBoundary();
        int xUpperBoundaryIndex = circle.getXUpperBoundary();

        // Get boundaries in y-direction in terms of index
        int yLowerBoundaryIndex = circle.getYLowerBoundary();
        int yUpperBoundaryIndex = circle.getYUpperBoundary();

        // Define offset for the filter for mapping the index of the data grid
        int xFilterOffset = 0 - (currentXIndex + xLowerBoundaryIndex);
        int yFilterOffset = 0 - (currentYIndex + yLowerBoundaryIndex);
        
        switch (operator)
        {
            case OPERATOR_ADD:
                for ( int j = currentYIndex + yLowerBoundaryIndex; j <= currentYIndex + yUpperBoundaryIndex; j++)
                    for ( int i = currentXIndex + xLowerBoundaryIndex; i <= currentXIndex + xUpperBoundaryIndex; i++)
                    {
                        if ((( i >= 0) && (i < x)) && (( j >= 0) && (j < y)))
                            if ((grid.getElement(i,j) != noData) && circle.getFilterElement(i + xFilterOffset, j + yFilterOffset))
                                result += grid.getElement(i,j);
                    }
            break;
            
            default:
                
                float temp[] = new float[x*y];
                int count = 0;
                
                for ( int j = currentYIndex + yLowerBoundaryIndex; j <= currentYIndex + yUpperBoundaryIndex; j++)
                    for ( int i = currentXIndex + xLowerBoundaryIndex; i <= currentXIndex + xUpperBoundaryIndex; i++)
                    {
                        if ((( i >= 0) && (i < x)) && (( j >= 0) && (j < y)))
                            if ((grid.getElement(i,j) != noData) && circle.getFilterElement(i + xFilterOffset, j + yFilterOffset))
                                temp[count++] = grid.getElement(i,j);
                    }
                
                if (operator == OPERATOR_MEAN)
                    result = StatisticalOperator.mean(temp,count);
                if (operator == OPERATOR_MEDIAN)
                    result = StatisticalOperator.median(temp,count);
                if (operator == OPERATOR_MAXIMUM)
                    result = StatisticalOperator.max(temp,count);
                if (operator == OPERATOR_MINIMUM)
                    result = StatisticalOperator.min(temp);
                if (operator == OPERATOR_RANGE)
                    result = StatisticalOperator.max(temp,count) - StatisticalOperator.min(temp);
                if (operator == OPERATOR_VARIANCE)
                    result = StatisticalOperator.variance(temp,count);
                
            break;
        }

        return result;
    }

    // Function to perform the focal calculation for a one element of a timecube object
    // CurrentXIndex, CurrentYIndex and currentTIndex defines the location of the element.
    // XYTCircle specifies the neighborhood where the calculation is being performed on.
    public float execute(int operator, MMATimeCube cube, int currentXIndex, int currentYIndex, int currentTIndex, XYTCircle circle)
    {
        float result = 0;
        int x = cube.getX();
        int y = cube.getY();
        int t = cube.getT();
        float noData = cube.getNoData();
        
        // Defines the boundaries of the focal neighborhood
        // where the center of the neighborhood defines the origin
        // i.e. the indexes are zero
        
        // Get boundaries in x-direction in terms of index
        int xLowerBoundaryIndex = circle.getXLowerBoundary();
        int xUpperBoundaryIndex = circle.getXUpperBoundary();

        // Get boundaries in y-direction in terms of index
        int yLowerBoundaryIndex = circle.getYLowerBoundary();
        int yUpperBoundaryIndex = circle.getYUpperBoundary();

        // Get boundaries in t-direction in terms of index
        int tLowerBoundaryIndex = circle.getTLowerBoundary();
        int tUpperBoundaryIndex = circle.getTUpperBoundary();

        // Define offset for the filter for mapping the index of the data cube
        int xFilterOffset = 0 - (currentXIndex + xLowerBoundaryIndex);
        int yFilterOffset = 0 - (currentYIndex + yLowerBoundaryIndex);
        int tFilterOffset = 0 - (currentTIndex + tLowerBoundaryIndex);
        
        switch (operator)
        {
            case OPERATOR_ADD:
                for ( int k = currentTIndex + tLowerBoundaryIndex; k <= currentTIndex + tUpperBoundaryIndex; k++)
                    for ( int j = currentYIndex + yLowerBoundaryIndex; j <= currentYIndex + yUpperBoundaryIndex; j++)
                        for ( int i = currentXIndex + xLowerBoundaryIndex; i <= currentXIndex + xUpperBoundaryIndex; i++)
                        {
                            if ((( i >= 0) && (i < x)) && (( j >= 0) && (j < y)) && (( k >= 0) && (k < t)))
                                if ((cube.getElement(i,j,k) != noData) && (circle.getFilterElement(i + xFilterOffset, j + yFilterOffset, k + tFilterOffset)))
                                    result += cube.getElement(i,j,k);
                        }
            break;
            
            default:

                float temp[] = new float[x*y*t];
                int count = 0;
                
                for ( int k = currentTIndex + tLowerBoundaryIndex; k <= currentTIndex + tUpperBoundaryIndex; k++)
                    for ( int j = currentYIndex + yLowerBoundaryIndex; j <= currentYIndex + yUpperBoundaryIndex; j++)
                        for ( int i = currentXIndex + xLowerBoundaryIndex; i <= currentXIndex + xUpperBoundaryIndex; i++)
                        {
                            if ((( i >= 0) && (i < x)) && (( j >= 0) && (j < y)) && (( k >= 0) && (k < t)))
                                if ((cube.getElement(i,j,k) != noData) && (circle.getFilterElement(i + xFilterOffset, j + yFilterOffset, k + tFilterOffset)))
                                    temp[count++] = cube.getElement(i,j,k);
                        }
                
                if (operator == OPERATOR_MEAN)
                    result = StatisticalOperator.mean(temp,count);
                if (operator == OPERATOR_MEDIAN)
                    result = StatisticalOperator.median(temp,count);
                if (operator == OPERATOR_MAXIMUM)
                    result = StatisticalOperator.max(temp,count);
                if (operator == OPERATOR_MINIMUM)
                    result = StatisticalOperator.min(temp);
                if (operator == OPERATOR_RANGE)
                    result = StatisticalOperator.max(temp,count) - StatisticalOperator.min(temp);
                if (operator == OPERATOR_VARIANCE)
                    result = StatisticalOperator.variance(temp,count);
    
            break;
        }
        
        return result;
    }

    // Function to perform the focal calculation for a one element of a spacecube object
    // CurrentXIndex, CurrentYIndex and currentZIndex defines the location of the element.
    // XYZCirclee specifies the neighborhood where the calculation is being performed on.
    public float execute( int operator, MMASpaceCube cube, int currentXIndex, int currentYIndex, int currentZIndex, XYZCircle circle)
    {
        float result = 0;
        int x = cube.getX();
        int y = cube.getY();
        int z = cube.getZ();
        float noData = cube.getNoData();
        
        // Defines the boundaries of the focal neighborhood
        // where the center of the neighborhood defines the origin
        // i.e. the indexes are zero
        
        // Get boundaries in x-direction in terms of index
        int xLowerBoundaryIndex = circle.getXLowerBoundary();
        int xUpperBoundaryIndex = circle.getXUpperBoundary();

        // Get boundaries in y-direction in terms of index
        int yLowerBoundaryIndex = circle.getYLowerBoundary();
        int yUpperBoundaryIndex = circle.getYUpperBoundary();

        // Get boundaries in z-direction in terms of index
        int zLowerBoundaryIndex = circle.getZLowerBoundary();
        int zUpperBoundaryIndex = circle.getZUpperBoundary();

        // Define offset for the filter for mapping the index of the data cube
        int xFilterOffset = 0 - (currentXIndex + xLowerBoundaryIndex);
        int yFilterOffset = 0 - (currentYIndex + yLowerBoundaryIndex);
        int zFilterOffset = 0 - (currentZIndex + zLowerBoundaryIndex);

        switch (operator)
        {
            case OPERATOR_ADD:
                for ( int k = currentZIndex + zLowerBoundaryIndex; k <= currentZIndex + zUpperBoundaryIndex; k++)
                    for ( int j = currentYIndex + yLowerBoundaryIndex; j <= currentYIndex + yUpperBoundaryIndex; j++)
                        for ( int i = currentXIndex + xLowerBoundaryIndex; i <= currentXIndex + xUpperBoundaryIndex; i++)
                        {
                            if ((( i >= 0) && (i < x)) && (( j >= 0) && (j < y)) && (( k >= 0) && (k < z)))
                                if ((cube.getElement(i,j,k) != noData) && (circle.getFilterElement(i + xFilterOffset, j + yFilterOffset, k + zFilterOffset)))
                                    result += cube.getElement(i,j,k);
                        }
            break;

            default:

                float temp[] = new float[x*y*z];
                int count = 0;
                
                for ( int k = currentZIndex + zLowerBoundaryIndex; k <= currentZIndex + zUpperBoundaryIndex; k++)
                    for ( int j = currentYIndex + yLowerBoundaryIndex; j <= currentYIndex + yUpperBoundaryIndex; j++)
                        for ( int i = currentXIndex + xLowerBoundaryIndex; i <= currentXIndex + xUpperBoundaryIndex; i++)
                        {
                            if ((( i >= 0) && (i < x)) && (( j >= 0) && (j < y)) && (( k >= 0) && (k < z)))
                                if ((cube.getElement(i,j,k) != noData) && (circle.getFilterElement(i + xFilterOffset, j + yFilterOffset, k + zFilterOffset)))
                                    temp[count++] = cube.getElement(i,j,k);
                        }
                
                if (operator == OPERATOR_MEAN)
                    result = StatisticalOperator.mean(temp,count);
                if (operator == OPERATOR_MEDIAN)
                    result = StatisticalOperator.median(temp,count);
                if (operator == OPERATOR_MAXIMUM)
                    result = StatisticalOperator.max(temp,count);
                if (operator == OPERATOR_MINIMUM)
                    result = StatisticalOperator.min(temp);
                if (operator == OPERATOR_RANGE)
                    result = StatisticalOperator.max(temp,count) - StatisticalOperator.min(temp);
                if (operator == OPERATOR_VARIANCE)
                    result = StatisticalOperator.variance(temp,count);
    
            break;
        }

        return result;
    }

    // Function to perform the focal calculation for a one element of a hypercube object
    // CurrentXIndex, CurrentYIndex and currentZIndex defines the location of the element.
    // XYZTCircle specifies the neighborhood where the calculation is being performed on.
    public float execute( int operator, MMAHyperCube cube, int currentXIndex, int currentYIndex, int currentZIndex, int currentTIndex, XYZTCircle circle)
    {
        float result = 0;
        int x = cube.getX();
        int y = cube.getY();
        int z = cube.getZ();
        int t = cube.getT();
        float noData = cube.getNoData();
        
        // Defines the boundaries of the focal neighborhood
        // where the center of the neighborhood defines the origin
        // i.e. the indexes are zero
        
        // Get boundaries in x-direction in terms of index
        int xLowerBoundaryIndex = circle.getXLowerBoundary();
        int xUpperBoundaryIndex = circle.getXUpperBoundary();

        // Get boundaries in y-direction in terms of index
        int yLowerBoundaryIndex = circle.getYLowerBoundary();
        int yUpperBoundaryIndex = circle.getYUpperBoundary();

        // Get boundaries in z-direction in terms of index
        int zLowerBoundaryIndex = circle.getZLowerBoundary();
        int zUpperBoundaryIndex = circle.getZUpperBoundary();

        // Get boundaries in t-direction in terms of index
        int tLowerBoundaryIndex = circle.getTLowerBoundary();
        int tUpperBoundaryIndex = circle.getTUpperBoundary();

        // Define offset for the filter for mapping the index of the data cube
        int xFilterOffset = 0 - (currentXIndex + xLowerBoundaryIndex);
        int yFilterOffset = 0 - (currentYIndex + yLowerBoundaryIndex);
        int zFilterOffset = 0 - (currentZIndex + zLowerBoundaryIndex);
        int tFilterOffset = 0 - (currentTIndex + tLowerBoundaryIndex);

        switch (operator)
        {
            case OPERATOR_ADD:
                for ( int l = currentTIndex + tLowerBoundaryIndex; l <= currentTIndex + tUpperBoundaryIndex; l++)
                    for ( int k = currentZIndex + zLowerBoundaryIndex; k <= currentZIndex + zUpperBoundaryIndex; k++)
                        for ( int j = currentYIndex + yLowerBoundaryIndex; j <= currentYIndex + yUpperBoundaryIndex; j++)
                            for ( int i = currentXIndex + xLowerBoundaryIndex; i <= currentXIndex + xUpperBoundaryIndex; i++)
                            {
                                if ((( i >= 0) && (i < x)) && (( j >= 0) && (j < y)) && (( k >= 0) && (k < z)) && (( l >= 0) && (l < t)))
                                    if ((cube.getElement(i,j,k,l) != noData) && (circle.getFilterElement(i + xFilterOffset, j + yFilterOffset, k + zFilterOffset, l + tFilterOffset)))
                                        result += cube.getElement(i,j,k,l);
                            }
            break;

            default:
                
                float temp[] = new float[x*y*z*t];
                int count = 0;

                for ( int l = currentTIndex + tLowerBoundaryIndex; l <= currentTIndex + tUpperBoundaryIndex; l++)
                    for ( int k = currentZIndex + zLowerBoundaryIndex; k <= currentZIndex + zUpperBoundaryIndex; k++)
                        for ( int j = currentYIndex + yLowerBoundaryIndex; j <= currentYIndex + yUpperBoundaryIndex; j++)
                            for ( int i = currentXIndex + xLowerBoundaryIndex; i <= currentXIndex + xUpperBoundaryIndex; i++)
                            {
                                if ((( i >= 0) && (i < x)) && (( j >= 0) && (j < y)) && (( k >= 0) && (k < z)) && (( l >= 0) && (l < t)))
                                    if ((cube.getElement(i,j,k,l) != noData) && (circle.getFilterElement(i + xFilterOffset, j + yFilterOffset, k + zFilterOffset, l + tFilterOffset)))
                                        temp[count++] = cube.getElement(i,j,k,l);
                            }
                
                if (operator == OPERATOR_MEAN)
                    result = StatisticalOperator.mean(temp,count);
                if (operator == OPERATOR_MEDIAN)
                    result = StatisticalOperator.median(temp,count);
                if (operator == OPERATOR_MAXIMUM)
                    result = StatisticalOperator.max(temp,count);
                if (operator == OPERATOR_MINIMUM)
                    result = StatisticalOperator.min(temp);
                if (operator == OPERATOR_RANGE)
                    result = StatisticalOperator.max(temp,count) - StatisticalOperator.min(temp);
                if (operator == OPERATOR_VARIANCE)
                    result = StatisticalOperator.variance(temp,count);
    
            break;
        }

        return result;
    }

    // Beginning of custom neighborhood type
    
    // Function to perform the focal calculation for a one element of a grid object
    // CurrentXIndex and CurrentYIndex defines the location of the element.
    // XYCustomNeighborhood specifies the neighborhood where the calculation is being performed on.
    public float execute( int operator, MMAGrid grid, int currentXIndex, int currentYIndex, XYCustomNeighborhood customhood)
    {
        float result = 0;
        int x = grid.getX();
        int y = grid.getY();
        float noData = grid.getNoData();
        
        // Defines the boundaries of the focal neighborhood
        // where the center of the neighborhood defines the origin
        // i.e. the indexes are zero
        
        // Get boundaries in x-direction in terms of index
        int xLowerBoundaryIndex = customhood.getXLowerBoundary();
        int xUpperBoundaryIndex = customhood.getXUpperBoundary();

        // Get boundaries in y-direction in terms of index
        int yLowerBoundaryIndex = customhood.getYLowerBoundary();
        int yUpperBoundaryIndex = customhood.getYUpperBoundary();

        // Define offset for the filter for mapping the index of the data grid
        int xFilterOffset = 0 - (currentXIndex + xLowerBoundaryIndex);
        int yFilterOffset = 0 - (currentYIndex + yLowerBoundaryIndex);
        
        switch (operator)
        {
            case OPERATOR_ADD:
                for ( int j = currentYIndex + yLowerBoundaryIndex; j <= currentYIndex + yUpperBoundaryIndex; j++)
                    for ( int i = currentXIndex + xLowerBoundaryIndex; i <= currentXIndex + xUpperBoundaryIndex; i++)
                    {
                        if ((( i >= 0) && (i < x)) && (( j >= 0) && (j < y)))
                            if ((grid.getElement(i,j) != noData) && customhood.getFilterElement(i + xFilterOffset, j + yFilterOffset))
                                result += grid.getElement(i,j);
                    }
            break;
            
            default:
                
                float temp[] = new float[x*y];
                int count = 0;
                
                for ( int j = currentYIndex + yLowerBoundaryIndex; j <= currentYIndex + yUpperBoundaryIndex; j++)
                    for ( int i = currentXIndex + xLowerBoundaryIndex; i <= currentXIndex + xUpperBoundaryIndex; i++)
                    {
                        if ((( i >= 0) && (i < x)) && (( j >= 0) && (j < y)))
                            if ((grid.getElement(i,j) != noData) && customhood.getFilterElement(i + xFilterOffset, j + yFilterOffset))
                                temp[count++] = grid.getElement(i,j);
                    }
                
                if (operator == OPERATOR_MEAN)
                    result = StatisticalOperator.mean(temp,count);
                if (operator == OPERATOR_MEDIAN)
                    result = StatisticalOperator.median(temp,count);
                if (operator == OPERATOR_MAXIMUM)
                    result = StatisticalOperator.max(temp,count);
                if (operator == OPERATOR_MINIMUM)
                    result = StatisticalOperator.min(temp);
                if (operator == OPERATOR_RANGE)
                    result = StatisticalOperator.max(temp,count) - StatisticalOperator.min(temp);
                if (operator == OPERATOR_VARIANCE)
                    result = StatisticalOperator.variance(temp,count);
                
            break;
        }

        return result;
    }

    // Function to perform the focal calculation for a one element of a timecube object
    // CurrentXIndex, CurrentYIndex and currentTIndex defines the location of the element.
    // XYTNeighborhood specifies the neighborhood where the calculation is being performed on.
    public float execute(int operator, MMATimeCube cube, int currentXIndex, int currentYIndex, int currentTIndex, XYTCustomNeighborhood customhood)
    {
        float result = 0;
        int x = cube.getX();
        int y = cube.getY();
        int t = cube.getT();
        float noData = cube.getNoData();
        
        // Defines the boundaries of the focal neighborhood
        // where the center of the neighborhood defines the origin
        // i.e. the indexes are zero
        
        // Get boundaries in x-direction in terms of index
        int xLowerBoundaryIndex = customhood.getXLowerBoundary();
        int xUpperBoundaryIndex = customhood.getXUpperBoundary();

        // Get boundaries in y-direction in terms of index
        int yLowerBoundaryIndex = customhood.getYLowerBoundary();
        int yUpperBoundaryIndex = customhood.getYUpperBoundary();

        // Get boundaries in t-direction in terms of index
        int tLowerBoundaryIndex = customhood.getTLowerBoundary();
        int tUpperBoundaryIndex = customhood.getTUpperBoundary();

        // Define offset for the filter for mapping the index of the data cube
        int xFilterOffset = 0 - (currentXIndex + xLowerBoundaryIndex);
        int yFilterOffset = 0 - (currentYIndex + yLowerBoundaryIndex);
        int tFilterOffset = 0 - (currentTIndex + tLowerBoundaryIndex);
        
        switch (operator)
        {
            case OPERATOR_ADD:
                for ( int k = currentTIndex + tLowerBoundaryIndex; k <= currentTIndex + tUpperBoundaryIndex; k++)
                    for ( int j = currentYIndex + yLowerBoundaryIndex; j <= currentYIndex + yUpperBoundaryIndex; j++)
                        for ( int i = currentXIndex + xLowerBoundaryIndex; i <= currentXIndex + xUpperBoundaryIndex; i++)
                        {
                            if ((( i >= 0) && (i < x)) && (( j >= 0) && (j < y)) && (( k >= 0) && (k < t)))
                                if ((cube.getElement(i,j,k) != noData) && (customhood.getFilterElement(i + xFilterOffset, j + yFilterOffset, k + tFilterOffset)))
                                    result += cube.getElement(i,j,k);
                        }
            break;
            
            default:

                float temp[] = new float[x*y*t];
                int count = 0;
                
                for ( int k = currentTIndex + tLowerBoundaryIndex; k <= currentTIndex + tUpperBoundaryIndex; k++)
                    for ( int j = currentYIndex + yLowerBoundaryIndex; j <= currentYIndex + yUpperBoundaryIndex; j++)
                        for ( int i = currentXIndex + xLowerBoundaryIndex; i <= currentXIndex + xUpperBoundaryIndex; i++)
                        {
                            if ((( i >= 0) && (i < x)) && (( j >= 0) && (j < y)) && (( k >= 0) && (k < t)))
                                if ((cube.getElement(i,j,k) != noData) && (customhood.getFilterElement(i + xFilterOffset, j + yFilterOffset, k + tFilterOffset)))
                                    temp[count++] = cube.getElement(i,j,k);
                        }
                
                if (operator == OPERATOR_MEAN)
                    result = StatisticalOperator.mean(temp,count);
                if (operator == OPERATOR_MEDIAN)
                    result = StatisticalOperator.median(temp,count);
                if (operator == OPERATOR_MAXIMUM)
                    result = StatisticalOperator.max(temp,count);
                if (operator == OPERATOR_MINIMUM)
                    result = StatisticalOperator.min(temp);
                if (operator == OPERATOR_RANGE)
                    result = StatisticalOperator.max(temp,count) - StatisticalOperator.min(temp);
                if (operator == OPERATOR_VARIANCE)
                    result = StatisticalOperator.variance(temp,count);
    
            break;
        }
        
        return result;
    }

    // Function to perform the focal calculation for a one element of a spacecube object
    // CurrentXIndex, CurrentYIndex and currentZIndex defines the location of the element.
    // XYZCustomNeighborhood specifies the neighborhood where the calculation is being performed on.
    public float execute( int operator, MMASpaceCube cube, int currentXIndex, int currentYIndex, int currentZIndex, XYZCustomNeighborhood customhood)
    {
        float result = 0;
        int x = cube.getX();
        int y = cube.getY();
        int z = cube.getZ();
        float noData = cube.getNoData();
        
        // Defines the boundaries of the focal neighborhood
        // where the center of the neighborhood defines the origin
        // i.e. the indexes are zero
        
        // Get boundaries in x-direction in terms of index
        int xLowerBoundaryIndex = customhood.getXLowerBoundary();
        int xUpperBoundaryIndex = customhood.getXUpperBoundary();

        // Get boundaries in y-direction in terms of index
        int yLowerBoundaryIndex = customhood.getYLowerBoundary();
        int yUpperBoundaryIndex = customhood.getYUpperBoundary();

        // Get boundaries in z-direction in terms of index
        int zLowerBoundaryIndex = customhood.getZLowerBoundary();
        int zUpperBoundaryIndex = customhood.getZUpperBoundary();

        // Define offset for the filter for mapping the index of the data cube
        int xFilterOffset = 0 - (currentXIndex + xLowerBoundaryIndex);
        int yFilterOffset = 0 - (currentYIndex + yLowerBoundaryIndex);
        int zFilterOffset = 0 - (currentZIndex + zLowerBoundaryIndex);

        switch (operator)
        {
            case OPERATOR_ADD:
                for ( int k = currentZIndex + zLowerBoundaryIndex; k <= currentZIndex + zUpperBoundaryIndex; k++)
                    for ( int j = currentYIndex + yLowerBoundaryIndex; j <= currentYIndex + yUpperBoundaryIndex; j++)
                        for ( int i = currentXIndex + xLowerBoundaryIndex; i <= currentXIndex + xUpperBoundaryIndex; i++)
                        {
                            if ((( i >= 0) && (i < x)) && (( j >= 0) && (j < y)) && (( k >= 0) && (k < z)))
                                if ((cube.getElement(i,j,k) != noData) && (customhood.getFilterElement(i + xFilterOffset, j + yFilterOffset, k + zFilterOffset)))
                                    result += cube.getElement(i,j,k);
                        }
            break;

            default:

                float temp[] = new float[x*y*z];
                int count = 0;
                
                for ( int k = currentZIndex + zLowerBoundaryIndex; k <= currentZIndex + zUpperBoundaryIndex; k++)
                    for ( int j = currentYIndex + yLowerBoundaryIndex; j <= currentYIndex + yUpperBoundaryIndex; j++)
                        for ( int i = currentXIndex + xLowerBoundaryIndex; i <= currentXIndex + xUpperBoundaryIndex; i++)
                        {
                            if ((( i >= 0) && (i < x)) && (( j >= 0) && (j < y)) && (( k >= 0) && (k < z)))
                                if ((cube.getElement(i,j,k) != noData) && (customhood.getFilterElement(i + xFilterOffset, j + yFilterOffset, k + zFilterOffset)))
                                    temp[count++] = cube.getElement(i,j,k);
                        }
                
                if (operator == OPERATOR_MEAN)
                    result = StatisticalOperator.mean(temp,count);
                if (operator == OPERATOR_MEDIAN)
                    result = StatisticalOperator.median(temp,count);
                if (operator == OPERATOR_MAXIMUM)
                    result = StatisticalOperator.max(temp,count);
                if (operator == OPERATOR_MINIMUM)
                    result = StatisticalOperator.min(temp);
                if (operator == OPERATOR_RANGE)
                    result = StatisticalOperator.max(temp,count) - StatisticalOperator.min(temp);
                if (operator == OPERATOR_VARIANCE)
                    result = StatisticalOperator.variance(temp,count);
    
            break;
        }

        return result;
    }

    // Function to perform the focal calculation for a one element of a hypercube object
    // CurrentXIndex, CurrentYIndex and currentZIndex defines the location of the element.
    // XYZTCustomNeighborhood specifies the neighborhood where the calculation is being performed on.
    public float execute( int operator, MMAHyperCube cube, int currentXIndex, int currentYIndex, int currentZIndex, int currentTIndex, XYZTCustomNeighborhood customhood)
    {
        float result = 0;
        int x = cube.getX();
        int y = cube.getY();
        int z = cube.getZ();
        int t = cube.getT();
        float noData = cube.getNoData();
        
        // Defines the boundaries of the focal neighborhood
        // where the center of the neighborhood defines the origin
        // i.e. the indexes are zero
        
        // Get boundaries in x-direction in terms of index
        int xLowerBoundaryIndex = customhood.getXLowerBoundary();
        int xUpperBoundaryIndex = customhood.getXUpperBoundary();

        // Get boundaries in y-direction in terms of index
        int yLowerBoundaryIndex = customhood.getYLowerBoundary();
        int yUpperBoundaryIndex = customhood.getYUpperBoundary();

        // Get boundaries in z-direction in terms of index
        int zLowerBoundaryIndex = customhood.getZLowerBoundary();
        int zUpperBoundaryIndex = customhood.getZUpperBoundary();

        // Get boundaries in t-direction in terms of index
        int tLowerBoundaryIndex = customhood.getTLowerBoundary();
        int tUpperBoundaryIndex = customhood.getTUpperBoundary();

        // Define offset for the filter for mapping the index of the data cube
        int xFilterOffset = 0 - (currentXIndex + xLowerBoundaryIndex);
        int yFilterOffset = 0 - (currentYIndex + yLowerBoundaryIndex);
        int zFilterOffset = 0 - (currentZIndex + zLowerBoundaryIndex);
        int tFilterOffset = 0 - (currentTIndex + tLowerBoundaryIndex);

        switch (operator)
        {
            case OPERATOR_ADD:
                for ( int l = currentTIndex + tLowerBoundaryIndex; l <= currentTIndex + tUpperBoundaryIndex; l++)
                    for ( int k = currentZIndex + zLowerBoundaryIndex; k <= currentZIndex + zUpperBoundaryIndex; k++)
                        for ( int j = currentYIndex + yLowerBoundaryIndex; j <= currentYIndex + yUpperBoundaryIndex; j++)
                            for ( int i = currentXIndex + xLowerBoundaryIndex; i <= currentXIndex + xUpperBoundaryIndex; i++)
                            {
                                if ((( i >= 0) && (i < x)) && (( j >= 0) && (j < y)) && (( k >= 0) && (k < z)) && (( l >= 0) && (l < t)))
                                    if ((cube.getElement(i,j,k,l) != noData) && (customhood.getFilterElement(i + xFilterOffset, j + yFilterOffset, k + zFilterOffset, l + tFilterOffset)))
                                        result += cube.getElement(i,j,k,l);
                            }
            break;

            default:
                
                float temp[] = new float[x*y*z*t];
                int count = 0;

                for ( int l = currentTIndex + tLowerBoundaryIndex; l <= currentTIndex + tUpperBoundaryIndex; l++)
                    for ( int k = currentZIndex + zLowerBoundaryIndex; k <= currentZIndex + zUpperBoundaryIndex; k++)
                        for ( int j = currentYIndex + yLowerBoundaryIndex; j <= currentYIndex + yUpperBoundaryIndex; j++)
                            for ( int i = currentXIndex + xLowerBoundaryIndex; i <= currentXIndex + xUpperBoundaryIndex; i++)
                            {
                                if ((( i >= 0) && (i < x)) && (( j >= 0) && (j < y)) && (( k >= 0) && (k < z)) && (( l >= 0) && (l < t)))
                                    if ((cube.getElement(i,j,k,l) != noData) && (customhood.getFilterElement(i + xFilterOffset, j + yFilterOffset, k + zFilterOffset, l + tFilterOffset)))
                                        temp[count++] = cube.getElement(i,j,k,l);
                            }
                
                if (operator == OPERATOR_MEAN)
                    result = StatisticalOperator.mean(temp,count);
                if (operator == OPERATOR_MEDIAN)
                    result = StatisticalOperator.median(temp,count);
                if (operator == OPERATOR_MAXIMUM)
                    result = StatisticalOperator.max(temp,count);
                if (operator == OPERATOR_MINIMUM)
                    result = StatisticalOperator.min(temp);
                if (operator == OPERATOR_RANGE)
                    result = StatisticalOperator.max(temp,count) - StatisticalOperator.min(temp);
                if (operator == OPERATOR_VARIANCE)
                    result = StatisticalOperator.variance(temp,count);
    
            break;
        }

        return result;
    }

}
